using System;
using System.Collections.Generic;
using System.Linq;
using Magiq.Behaviors;
using Magiq.Delete;
using Magiq.Insert;
using Magiq.Providers.Objects;
using Magiq.Query;
using Magiq.Support;
using Magiq.Update;

namespace Magiq
{
    public static class Magiq
    {
        private static List<MagiqProvider> providers;

        static Magiq()
        {
            Clear();
        }

        public static IMagiqBehavior BehaviorChain { get; private set; }

        private static TStrategy GetStrategy<TStrategy>(Func<MagiqProvider, TStrategy> getStrategy)
            where TStrategy : class
        {
            return providers.Select(getStrategy).First(x => x != null);
        }

        public static IUpdateStrategy ForUpdate<T>(IUpdate<T> update) where T : class
        {
            return GetStrategy(x => x.GetUpdateStrategy(update));
        }

        public static IDeleteStrategy ForDelete<T>(IDelete<T> delete) where T : class
        {
            return GetStrategy(x => x.GetDeleteStrategy(delete));
        }

        public static IQueryStrategy<TChild> ForQuery<T, TChild>(QueryInfo<T, TChild> queryInfo) where T : class 
        {
            return GetStrategy(x => x.GetQueryStrategy(queryInfo));
        }

        public static IInsertStrategy ForInsert<TSource, TDestination, TNew>(IInsert<TSource, TDestination, TNew> insert)
            where TDestination : class where TNew : class, TDestination, new()
        {
            return GetStrategy(x => x.GetInsertStrategy(insert));
        }

        public static IInsertDestination<TNew> ForInsertDestination<TSource, TDestination, TNew>(
            IInsert<TSource, TDestination, TNew> insertDestinationDefinition)
            where TDestination : class
            where TNew : class, TDestination, new()
        {
            return GetStrategy(x => x.GetInsertDestination(insertDestinationDefinition));
        }

        public static IMagiqConfigurator Register<TMagiq>() where TMagiq : MagiqProvider, new()
        {
            return new Configurator().Register<TMagiq>();
        }

        public static IMagiqConfigurator Register(MagiqProvider magiq)
        {
            return new Configurator().Register(magiq);
        }

        public static IMagiqConfigurator Clear()
        {
            providers = new List<MagiqProvider>
                            {
                                new MagiqToObjects()
                            };
            BehaviorChain = new NullBehavior();
            return new Configurator();
        }

        public static IMagiqConfigurator Use<TBehavior>() where TBehavior : IMagiqBehavior, new()
        {
            return new Configurator().Use<TBehavior>();
        }

        public static IMagiqConfigurator Use(IMagiqBehavior behavior)
        {
            return new Configurator().Use(behavior);
        }

        #region Nested type: Configurator

        private class Configurator : IMagiqConfigurator
        {
            #region IMagiqConfigurator Members

            public IMagiqConfigurator Use<TBehavior>() where TBehavior : IMagiqBehavior, new()
            {
                return Use(new TBehavior());
            }

            public IMagiqConfigurator Use(IMagiqBehavior behavior)
            {
                BehaviorChain.NextBehavior = behavior;
                return this;
            }

            #endregion

            public IMagiqConfigurator Register<TMagiq>() where TMagiq : MagiqProvider, new()
            {
                return Register(new TMagiq());
            }

            public IMagiqConfigurator Register(MagiqProvider magiq)
            {
                if (magiq.OrderStrategy == MagiqProviderOrderStrategy.Default)
                {
                    providers.Remove(providers.Last());
                    providers.Add(magiq);
                }
                else
                {
                    providers.Add(magiq);
                    providers.Sort();
                }
                return this;
            }
        }

        #endregion
    }
}